﻿//===============================================================================
// Microsoft Innovation Centre - Western Europe
// Copyright (c) 2008 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.WVB.Silverlight.Infrastructure;

namespace Microsoft.WVB.Framework.AdControls
{
    /// <summary>
    /// An <see cref="AdBase"/> derived control intended to display Video Ads.
    /// </summary>
    /// <remarks>When Templating this control, you will need to modify the AdVideo.xaml file.
    /// This control has 3 controls part of its Template.
    /// A <see cref="Button"/> control named <b>CloseButton</b> used for Closing the Ad if allowed.
    /// A <see cref="MediaElement"/> control named <b>MediaContent</b> for displaying the Ad creative.
    /// A <see cref="ToggleButton"/> control named <b>MuteButton</b> for muting/unmuting the Video.
    /// </remarks>
    [TemplatePart(Name = AdVideo.CloseButtonName, Type = typeof(Button)),
    TemplatePart(Name = AdVideo.MuteButtonName, Type = typeof(ToggleButton)),
    TemplatePart(Name = AdVideo.MediaContentName, Type = typeof(MediaElement))]
    public sealed class AdVideo : AdBase, IDisposable
    {
        private const string CloseButtonName = "CloseButton";
        private const string MuteButtonName = "MuteButton";
        private const string MediaContentName = "MediaContent";

        internal Button CloseButton = null;
        internal ToggleButton MuteButton = null;
        internal MediaElement Media = null;

        public IPlayerModel ViewModel { get; set; }

        private RoutedEventHandler mediaOpenedHandler = null;
        private TimelineMarkerRoutedEventHandler mediaMarkerHandler = null;
        private RoutedEventHandler mediaEndedHandler = null;
        private RoutedEventHandler currentStateChangedHandler = null;
        private RoutedEventHandler bufferingChangedHandler = null;
        private MouseButtonEventHandler adClickHandler = null;
        private RoutedEventHandler closeHandler = null;

        private System.Action playAction = null;
        private System.Action closeAction = null;

        /// <summary>
        /// public constructor for <see cref="AdVideo"/>.
        /// </summary>
        public AdVideo()
            : base()
        {
            this.DefaultStyleKey = typeof(AdVideo);

            if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                ViewModel = IoC.GetCreate<IPlayerModel>();

                playAction = delegate()
                {
                    if (Media != null)
                    {
                        Media.Play();

                        if ((ViewModel != null) && (AdContext.DisplayRegion == AdOverlayMode.Inline))
                        {
                            ViewModel.Action(new Microsoft.WVB.Framework.ActionBag() { Action = Microsoft.WVB.Framework.PlayerAction.DisableCommandAndPause });
                        }
                    }
                };

                closeAction = delegate()
                {
                    if (Media != null)
                    {
                        Media.Stop();

                        if ((ViewModel != null) && (AdContext.DisplayRegion == AdOverlayMode.Inline))
                        {
                            ViewModel.Action(new Microsoft.WVB.Framework.ActionBag() { Action = Microsoft.WVB.Framework.PlayerAction.EnableCommandAndPlay });
                        }
                    }
                };
            }
        }

        /// <summary>
        /// Overriden from base class <see cref="BaseAd"/>. Executes all queued actions if any.
        /// </summary>
        /// <param name="oldValue">A <see cref="bool"/> representing the old value before the change.</param>
        /// <param name="newValue">A <see cref="bool"/> representing the new value after the change.</param>
        /// <remarks>Queued actions are present if the user interacts with the Ad placeholder even thought the Ad is not ready.
        /// i.e. the Video is not yet buffered but the user wants to Pause it or he wants to close it before it is displayed.</remarks>
        protected override void OnIsReadyChanged(bool oldValue, bool newValue)
        {
            if (newValue)
            {
                if (QueuedActions.Count > 0)
                {
                    for (int i = 0; i < QueuedActions.Count; i++)
			        {
                        System.Action action = QueuedActions.Dequeue();
                        this.Dispatcher.BeginInvoke(action);
                        //action.Invoke();

                        if (action == closeAction)
                            break;		 
			        }                        
               }
            }
        }

        /// <summary>
        /// Acquire the Control Template and the control instance mentioned in the Template Contract.
        /// AdVideo has a <see cref="MediaElement"/>, a <see cref="Button"/> and a <see cref="ToggleButton"/> controls in its contract.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            Media = base.GetTemplateChild(AdVideo.MediaContentName) as MediaElement;
            CloseButton = base.GetTemplateChild(AdVideo.CloseButtonName) as Button;
            MuteButton = base.GetTemplateChild(AdVideo.MuteButtonName) as ToggleButton;

            if (Media != null)
            {
                if (CloseButton != null)
                {
                    if (closeHandler == null)
                    {
                        closeHandler = delegate(object sender, RoutedEventArgs e)
                        {
                            OnAdStopped(this, new EventArgs());
                        };
                    }
                    CloseButton.Click += closeHandler;  
                }
            }
        }

        /// <summary>
        /// Overriden from base class <see cref="BaseAd"/>. Checks Template and interprets information
        /// contained in the <see cref="AdContext"/> paramter. It buffers Video in the MediaElement.
        /// </summary>
        /// <param name="adContext">A <see cref="AdContext"/> object containing all the information about the Ad.</param>
        public override void Buffer(AdContext adContext)
        {
            this.AdContext = adContext;

            if  ((Media != null) && (CloseButton != null) && (MuteButton != null))
            {
                Media.BufferingTime = AdManager.BufferingTime;
                Media.AutoPlay = true;
                Media.Volume = .5;

                if ((MuteButton != null) && (CloseButton != null))
                {
                    if (AdContext.DisplayRegion == AdOverlayMode.Inline)
                    {
                        MuteButton.Visibility = Visibility.Collapsed;
                        CloseButton.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        Media.IsMuted = true;
                        Binding mute = new Binding("IsMuted");
                        mute.Source = Media;
                        mute.Mode = BindingMode.TwoWay;
                        MuteButton.SetBinding(ToggleButton.IsCheckedProperty, mute);

                        CloseButton.IsEnabled = adContext.CanSkip;
                    }
                }

                if (bufferingChangedHandler == null)
                {
                    bufferingChangedHandler = delegate(object sender, RoutedEventArgs e)
                    {
                        if ((Media.BufferingProgress >= .5) && (!this.IsReady))
                        {
                            this.IsReady = true;
                            Media.BufferingProgressChanged -= bufferingChangedHandler;
                        }
                    };
                }
                Media.BufferingProgressChanged += bufferingChangedHandler;

                if (currentStateChangedHandler == null)
                {
                    currentStateChangedHandler = delegate(object sender, RoutedEventArgs e)
                    {
                        if (Media.CurrentState == MediaElementState.Opening)
                        {
                            // According to documentation, Pause will be queued. And when it tries to start
                            // automatically after Buffering, it will Pause.
                            Media.Pause();
                        }
                    };
                }
                Media.CurrentStateChanged += currentStateChangedHandler;

                if (mediaOpenedHandler == null)
                {
                    mediaOpenedHandler = delegate(object sender, RoutedEventArgs e)
                    {
                        // Workaround to make sure that this delegate is only executed one time !!
                        if (BeginReachCount < 1)
                        {
                            TimeSpan adHalfTime = TimeSpan.FromSeconds((Media.NaturalDuration.TimeSpan.TotalSeconds / 2));
                            TimelineMarker marker = new TimelineMarker();
                            marker.Text = AdVideo.AdMarkerText;
                            marker.Type = AdVideo.AdMarkerType;
                            marker.Time = adHalfTime;
                            Media.Markers.Add(marker);

                            BeginReachCount++;
                            Media.MediaOpened -= mediaOpenedHandler;
                        }
                    };
                }
                Media.MediaOpened += mediaOpenedHandler;

                if (mediaMarkerHandler == null)
                {
                    mediaMarkerHandler = delegate(object sender, TimelineMarkerRoutedEventArgs e)
                    {
                        // Workaround to make sure that this delegate is only executed one time !!
                        if (MidReachCount < 1)
                        {
                            if (e.Marker.Type == AdVideo.AdMarkerType)
                            {
                                this.OnAdImpressionTracked(this, new AdTrackedEventArgs(AdImpressionTiming.Middle));

                                MidReachCount++;
                                Media.MarkerReached -= mediaMarkerHandler;
                            }
                        }
                    };
                }
                Media.MarkerReached += mediaMarkerHandler;

                if (mediaEndedHandler == null)
                {
                    mediaEndedHandler = delegate(object sender, RoutedEventArgs e)
                    {
                        // Workaround to make sure that this delegate is only executed one time !!
                        if (EndReachCount < 1)
                        {
                            this.OnAdEnded(this, new EventArgs());
                            this.OnAdImpressionTracked(this, new AdTrackedEventArgs(AdImpressionTiming.End));
                            
                            EndReachCount++;
                            Media.MediaEnded -= mediaEndedHandler;
                        }
                    };
                }
                Media.MediaEnded += mediaEndedHandler;


                if ((adClickHandler == null))
                {
                    adClickHandler = delegate(object sender, MouseButtonEventArgs e)
                    {
                        System.Windows.Browser.HtmlPage.Window.Navigate(adContext.Click, "_blank");
                    };
                }
                Media.MouseLeftButtonUp += adClickHandler;

                Media.Source = this.AdContext.Source;
            }
        }

        /// <summary>
        /// Overriden. Starts the <see cref="Action"/> defined as a starting Action in the constructor.
        /// </summary>
        public override void Start()
        {
            this.OnAdImpressionTracked(this, new AdTrackedEventArgs(AdImpressionTiming.Beginning));

            if (this.IsReady)
                playAction.Invoke();
            else
            {
                QueuedActions.Enqueue(playAction);
            }
        }

        /// <summary>
        /// Overriden. Executes the <see cref="Action"/> defined as the Stop action 
        /// (when a user explicitly stop the Ad). 
        /// </summary>
        /// <param name="sender">The <see cref="Object"/> which fired the event.</param>
        /// <param name="e">An <see cref="EventArgs"/> parameter.</param>
        protected override void OnAdStopped(object sender, EventArgs e)
        {
            if (this.IsReady)
                closeAction.Invoke();
            else
            {
                QueuedActions.Enqueue(closeAction);
            }

            base.OnAdStopped(sender, e);
        }

        /// <summary>
        /// Overriden. Executes the <see cref="Action"/> being defined as a Close action.
        /// </summary>
        /// <param name="sender">The <see cref="Object"/> which fired the event.</param>
        /// <param name="e">An <see cref="EventArgs"/> parameter.</param>
        protected override void OnAdEnded(object sender, EventArgs e)
        {
            if (this.IsReady)
                closeAction.Invoke();
            else
            {
                QueuedActions.Enqueue(closeAction);
            }

            base.OnAdEnded(sender, e);
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (Media != null)
            {
                Media.BufferingProgressChanged -= bufferingChangedHandler;
                Media.CurrentStateChanged -= currentStateChangedHandler;
                Media.MediaEnded -= mediaEndedHandler;
                Media.MediaOpened -= mediaOpenedHandler;
                Media.MarkerReached -= mediaMarkerHandler;
                Media.MouseLeftButtonUp -= adClickHandler;
            }
        }

        #endregion
    }
}
